package com.xcity.game.arena.offline;

import java.util.Calendar;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import com.xcity.db.entity.Player;
import com.xcity.db.entity.Ranking;
import com.xcity.game.arena.ArenaHandler;
import com.xcity.game.arena.ArenaUtils;
import com.xcity.game.common.Cause;
import com.xcity.game.common.MessageConst;
import com.xcity.game.common.PlayerPool;
import com.xcity.game.event.GameEvents;
import com.xcity.game.item.ItemService;
import com.xcity.game.mail.MailAttach;
import com.xcity.game.mail.MailService;
import com.xcity.game.player.cache.PlayerCacheService;
import com.xcity.game.rank.RankingList;
import com.xcity.game.rank.RankingListEnum;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.request.ArenaBuyTimesRequest;
import com.xcity.pomelo.request.ArenaClearCDRequest;
import com.xcity.pomelo.request.ArenaInfoRequest;
import com.xcity.pomelo.request.ArenaListRequest;
import com.xcity.pomelo.request.ArenaNearbyListRequest;
import com.xcity.pomelo.request.ArenaRewardRequest;
import com.xcity.pomelo.response.ArenaBuyTimesResponse;
import com.xcity.pomelo.response.ArenaListResponse;
import com.xcity.pomelo.response.HintResponse;
import com.xcity.pomelo.response.OfflineArenaInfoResponse;
import com.xcity.pomelo.response.Response;

import naga.x.App;
import naga.x.event.Event;
import naga.x.event.EventListener;
import naga.x.game.template.TemplateService;
import naga.x.game.time.DayListener;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.ServiceException;

/**
 * 离线竞技场
 * @author yang.li
 *
 */
@OPHandler
public class OfflineArenaHandler implements ArenaHandler, DayListener, EventListener {
	
	protected List<ArenaDailyRewardTemplate> rewards;
	protected long dailyRewardTime; // 每日奖励时间
	
	public static final int DAILY_REWARD_HOUR = 21; // 每天21点结算奖励

	@Override
	public void startup() throws ServiceException {
		rewards = App.getApp().getServiceManager().get(TemplateService.class).getTemplatesList(ArenaDailyRewardTemplate.class);
		for (int i = 0; i < rewards.size(); i++) {
			rewards.get(i).setId(i); // reset
		}
		resetDailyRewardTime();
		long delay = dailyRewardTime - TimeUpdater.getInstance().now();
		if (delay <= 0) {
			LOG.error("the offline arena schedule's delay is illegal:" + delay);
			System.exit(1);
		}
		// 每天21点结算
		App.getApp().getSchedule().scheduleAtFixedRate(new Runnable() {
			@Override
			public void run() {
				settlement();
			}
		}, delay, TimeUnit.DAYS.toMillis(1), TimeUnit.MILLISECONDS);
		TimeUpdater.getInstance().addDayListener(this);
		App.getApp().getEventManager().register(this);
	}

	@Override
	public void shutdown() throws ServiceException {
	}

	@Override
	public String getId() {
		return getClass().getName();
	}

	@Override
	public int[] getEventTypes() {
		return new int[] { GameEvents.EVENT_ARENA_END };
	}

	@Override
	public void handleEvent(Event event) {
		switch (event.getType()) {
			case GameEvents.EVENT_ARENA_END:
				combatEnd(event.getParameter(0), event.getParameter(1), event.getParameter(2));
				break;
		}
	}

	@Override
	public boolean dayChanged() {
		resetDailyRewardTime();
		return true;
	}
	
	@PomeloOP(PomeloRoute.ARENA_LIST)
	protected void requestList(Session session, ArenaListRequest req) {
		List<Ranking> rankings = RankingListEnum.ARENA.instance.getEntries();
		if (rankings != null && rankings.size() > 20) {
			rankings = rankings.subList(0, 20);
		}
		session.send(new ArenaListResponse(req.getSerial(), rankings));
	}
	
	@PomeloOP(PomeloRoute.ARENA_NEARBY_LIST)
	protected void requestNearby(Session session, ArenaNearbyListRequest req) {
		List<Ranking> list = match(session.getClient());
		session.send(new ArenaListResponse(req.getSerial(), list));
	}
	
	@PomeloOP(PomeloRoute.ARENA_INFO)
	protected void requestInfo(Session session, ArenaInfoRequest req) {
		Player player = session.getClient();
		Ranking r = RankingListEnum.ARENA.instance.findEntry(player);
//		Ranking r = session.getAttribute(RANKTYPE.ARENA.handler.getSessionKey());
		int rank = r != null ? r.getIndex() + 1 : -1;
//		ArenaScoreTemplate template = findTemplate(player.getPool().getIntValue(PlayerPool.PROPERTY_ARENA_SCORE));
		session.send(new OfflineArenaInfoResponse(req.getSerial(), session.getClient(), rank, null, this));
	}
	
	@PomeloOP(PomeloRoute.ARENA_REQUEST_REWARD)
	protected void requestReward(Session session, ArenaRewardRequest req) {
//		Player player = session.getClient();
//		int index = player.getPool().getInt(PlayerPool.PROPERTY_ARENA_DAILY_REWARD_ID, -1);
//		if (index < 0) {
//			session.send(new HintResponse(req.getSerial(), MessageConst.HAVE_NO_REWARD));
//			return;
//		}
//		player.getPool().remove(PlayerPool.PROPERTY_ARENA_DAILY_REWARD_ID);
//		if (index >= rewards.size()) {
//			session.send(new Response(req.getSerial()));
//			LOG.info("[ARENA REWARD ERROR]ID[{}]REWARD[{}]CAUSE[REWARD NOT FOUND]", player.getId(), index);
//			return;
//		}
//		ArenaDailyRewardTemplate reward = rewards.get(index);
//		player.addGold(reward.getGold(), Cause.ARENA);
//		player.addHonor(reward.getHonor(), Cause.ARENA);
		session.send(new Response(req.getSerial()));
	}
	
	@PomeloOP(PomeloRoute.ARENA_CLEAR_CD)
	protected void requestClearCD(Session session, ArenaClearCDRequest req) {
		Player player = session.getClient();
		String error = ArenaUtils.clearCD(player);
		if (error != null) {
			session.send(new HintResponse(req.getSerial(), error));
		} else {
			session.send(new Response(req.getSerial()));
		}
	}

	@PomeloOP(PomeloRoute.ARENA_BUY_TIMES)
	protected void requestBuyTimes(Session session, ArenaBuyTimesRequest req) {
		Player player = session.getClient();
		String error = ArenaUtils.buyTimes(player);
		if (error != null) {
			session.send(new HintResponse(req.getSerial(), error));
		} else {
			session.send(new ArenaBuyTimesResponse(req.getSerial(), player));
		}
	}
	
	private void resetDailyRewardTime() {
		Calendar cal = Calendar.getInstance();
		cal.set(Calendar.HOUR_OF_DAY, DAILY_REWARD_HOUR);
		cal.set(Calendar.MINUTE, 0);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);
		long t = cal.getTimeInMillis();
		if (t <= TimeUpdater.getInstance().now()) {
			cal.add(Calendar.DAY_OF_YEAR, 1);
			t = cal.getTimeInMillis();
		}
		dailyRewardTime = t;
	}
	
	private void settlement() {
		List<Ranking> rankings = RankingListEnum.ARENA.instance.getEntries();
//		ActorService service = App.getApp().getServiceManager().get(ActorService.class);
//		PlayerCacheService service = App.getApp().getServiceManager().get(PlayerCacheService.class);
		MailService mailService = App.getApp().getServiceManager().get(MailService.class);
		String title = MessageConst.OFFLINE_ARENA_DAILY_REWARD_MAIL_TITLE;
		String content = MessageConst.OFFLINE_ARENA_DAILY_REWARD_MAIL_CONTENT;
		for (int i = 0; i < rewards.size(); i++) {
			ArenaDailyRewardTemplate reward = rewards.get(i);
			for (int j = reward.getRankingFrom(); j <= reward.getRankingTo(); j++) {
				if (j >= rankings.size()) {
					return;
				}
				Ranking ranking = rankings.get(j);
				MailAttach attach = new MailAttach(reward.getGold(), 0);
				if (reward.getHonor() > 0) {
					attach.addItem(ItemService.honorDefault.getId(), reward.getHonor());
				}
				mailService.sendSystemMail(ranking.getPlayerId(), title, content, attach);
				LOG.info("[OFFLINE ARENA REWARDED]ID[{}]RANK[{}]REWARD[{}]", ranking.getPlayerId(), ranking.getIndex(), reward.getId());
//				Player player = ranking != null ? service.find(ranking.getPlayerId()) : null;
//				if (player != null) {
//					player.poolSet(PlayerPool.PROPERTY_ARENA_DAILY_REWARD_ID, reward.getId());
//					LOG.info("[ARENA REWARD TRY]ID[{}]RANK[{}]REWARD[{}]", player.getId(), ranking.getIndex(), reward.getId());
//				} else {
//					LOG.info("[ARENA REWARD ERROR]ID[{}]RANK[{}]CAUSE[PLAYER NULL]",
//							ranking != null ? ranking.getId() : 0,
//							ranking != null ? ranking.getIndex() : -1);
//				}
			}
		}
	}
	
	protected void combatEnd(Player player, long targetId, boolean isWin) {
		if (isWin) {
			combatWon(player);
			Player target = App.getApp().getServiceManager().get(PlayerCacheService.class).find(targetId);
			RankingList rl = RankingListEnum.ARENA.instance;
			Ranking ranking = rl.findEntry(player.getId());
			rl.swap(player, target);
			int rank = 0;
			if(ranking != null){
				rank = ranking.getIndex();
			}else{
				rank = 5000;
			}
			App.getApp().getEventManager().addEvent(GameEvents.EVENT_ARENA_WIN, player, rank);
		} else {
			combatLost(player);
		}
	}
	
	protected int combatWon(Player player) {
		player.poolAdd(PlayerPool.PROPERTY_ARENA_WON_NUM, 1);
		int repeatNum = player.poolAdd(PlayerPool.PROPERTY_ARENA_WON_REPEATLY_NUM, 1);
		player.poolSet(PlayerPool.PROPERTY_ARENA_LOST_REPEATLY_NUM, 0);
		
		player.addHonor(15, Cause.ARENA_WIN);
		LOG.info("[ARENA WIN]ID[{}]LVL[{}]SCORE1[{}]V1[{}]V2[{}]SCORE2[{}]", player.getId(), 0, 0, 0, 0, 0);
		return 0;
	}
	
	protected int combatLost(Player player) {
		player.poolAdd(PlayerPool.PROPERTY_ARENA_LOST_NUM, 1);
		int repeatNum = player.poolAdd(PlayerPool.PROPERTY_ARENA_LOST_REPEATLY_NUM, 1);
		player.poolSet(PlayerPool.PROPERTY_ARENA_WON_REPEATLY_NUM, 0);
		player.addHonor(1, Cause.ARENA_LOSE);
		LOG.info("[ARENA LOSE]ID[{}]LVL[{}]SCORE1[{}]V1[{}]V2[{}]SCORE2[{}]", player.getId(), 0, 0, 0, 0, 0);
		return 0;
	}
	
	public List<Ranking> match(Player player) {
		List<Ranking> rankings = RankingListEnum.ARENA.instance.getEntries();
		Ranking myRanking = RankingListEnum.ARENA.instance.findEntry(player);
		int index = myRanking != null ? myRanking.getIndex() : rankings.size();
		int gap = index / 5; // 间隔区间
		if (gap <= 0) {
			gap = 1;
		} else if (gap > 5) {
			gap = 5;
		}
		
		LinkedList<Ranking> players = new LinkedList<Ranking>();
		
		// 向前查找
		for (int i = index - 1; i >= 0 && players.size() < 5; i -= gap) {
			Ranking ranking = rankings.get(i);
			players.addFirst(ranking);
		}
		// 不足5名，向后查找
		for (int i = index + 1, size = rankings.size(); i < size && players.size() < 5; i++) {
			Ranking ranking = rankings.get(i);
			players.addLast(ranking);
		}
		
		return players;
	}
	
	/**
	 * 根据排名查找对应每日奖励
	 * @param rank
	 * @return
	 */
	public ArenaDailyRewardTemplate findDailyReward(int rank) {
		if (rank < 0) {
			return null;
		}
		List<ArenaDailyRewardTemplate> list = this.rewards;
		int start = 0, end = list.size() - 1;
		while (start <= end) {
			int mid = (start + end) >> 1;
			ArenaDailyRewardTemplate t = list.get(mid);
			if (rank > t.getRankingTo()) {
				start = mid + 1;
				continue;
			}
			if (rank < t.getRankingFrom()) {
				end = mid - 1;
				continue;
			}
			return t;
		}
		return null;
	}
	
	public ArenaDailyRewardTemplate getDailyRewardByIndex(int index) {
		return index >= 0 && index < rewards.size() ? rewards.get(index) : null;
	}
	
	public long getDailyRewardCD() {
		return dailyRewardTime - TimeUpdater.getInstance().now();
	}

}
